#ifdef COMPILE_OPENGL

#include "CBL_Debug.h"
#include "COGL_IndexBufferAtomic.h"
#include <OpenGL/OpenGL.h>
#include <OpenGL/glext.h>
#include "COGL_Extensions.h"

CB_SUBNAMESPACE_START(OpenGL)

// -----------------------------------------------------------------------

static GLenum s_NativeStorageType[RenderLib::CIndexBufferDescriptor::NumStorageTypes] =
{
	GL_STREAM_DRAW,
    GL_STATIC_DRAW,
    GL_DYNAMIC_DRAW
};

// -----------------------------------------------------------------------

#define ENUM_ITEM(_Name, _Size, _OpenGLFormat, _D3D9Format, _D3D10Format) _OpenGLFormat,
static GLenum s_NativeVertexFormat[RenderLib::CIndexBufferDescriptor::NumIndexFormats] =
{
	0,
	#include "CRL_IndexFormatsEnum.h"
};
#undef ENUM_ITEM

// -----------------------------------------------------------------------

#ifdef GL_VERSION_3_0
static GLbitfield s_NativeLockFlushType[RenderLib::IBufferAtomic::NumLockTypes] =
{
    GL_MAP_INVALIDATE_BUFFER_BIT,
    GL_MAP_INVALIDATE_RANGE_BIT,
    GL_MAP_READ,
    GL_MAP_READ | GL_MAP_INVALIDATE_RANGE_BIT,
};
#endif

// -----------------------------------------------------------------------

static GLenum s_NativeLockAccessType[RenderLib::IBufferAtomic::NumLockTypes] =
{
	GL_WRITE_ONLY,
    GL_WRITE_ONLY,
    GL_READ_ONLY,
    GL_READ_WRITE
};

// -----------------------------------------------------------------------

CIndexBufferAtomic::CIndexBufferAtomic(const RenderLib::CIndexBufferDescriptor& _rDescriptor)
: m_BufferResourceId(0)
, m_Descriptor(_rDescriptor)
{
	glGenBuffers(1, &m_BufferResourceId);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_Descriptor.GetSizeByte(), NULL, s_NativeStorageType[m_Descriptor.GetStorageType()]);
}

// -----------------------------------------------------------------------

CIndexBufferAtomic::~CIndexBufferAtomic()
{
    glDeleteBuffers(1, &m_BufferResourceId);
}

// -----------------------------------------------------------------------

void* CIndexBufferAtomic::Lock(RenderLib::IBufferAtomic::ELockType _LockType)
{
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_BufferResourceId);
    
    if (_LockType == RenderLib::IBufferAtomic::LockOverwrite)
    {
    	glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_Descriptor.GetSizeByte(), NULL, s_NativeStorageType[m_Descriptor.GetStorageType()]);
    }
    
    return glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, s_NativeLockAccessType[_LockType]);    
}

// -----------------------------------------------------------------------

void* CIndexBufferAtomic::Lock(RenderLib::IBufferAtomic::ELockType _LockType, const RenderLib::CRangeDescriptor& _rRange)
{
	Debug::Assert(CB_DEBUG_MSG(_rRange.GetOffset() != RenderLib::CRangeDescriptor::InvalidValue,
                               "To partially lock an indexbuffer provide Offset"));
    Debug::Assert(CB_DEBUG_MSG(_rRange.GetCount() != RenderLib::CRangeDescriptor::InvalidValue,
                               "To partially lock an indexbuffer provide Count"));
    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_BufferResourceId);
    
    if (_LockType == RenderLib::IBufferAtomic::LockOverwrite)
    {
    	glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, _rRange.GetOffset(), _rRange.GetCount(), NULL);	
    }
    
#ifdef GL_VERSION_3_0
    return glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, _rRange.GetOffset(), _rRange.GetCount(), s_NativeLockFlushType[_LockType]);
#endif
    
    return (uint8*)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, s_NativeLockAccessType[_LockType]) + _rRange.GetOffset();
}

// -----------------------------------------------------------------------

void CIndexBufferAtomic::Unlock()
{
    glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
}

// -----------------------------------------------------------------------

void CIndexBufferAtomic::Update(const void *_pData)
{
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_BufferResourceId);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_Descriptor.GetSizeByte(), _pData, s_NativeStorageType[m_Descriptor.GetStorageType()]);
}

// -----------------------------------------------------------------------

void CIndexBufferAtomic::Update(const void *_pData, const RenderLib::CRangeDescriptor& _rRange)
{
	Debug::Assert(CB_DEBUG_MSG(_rRange.GetOffset() != RenderLib::CRangeDescriptor::InvalidValue,
                               "To partially update an indexbuffer provide Offset"));
    Debug::Assert(CB_DEBUG_MSG(_rRange.GetCount() != RenderLib::CRangeDescriptor::InvalidValue,
                               "To partially update an indexbuffer provide Count"));
    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_BufferResourceId);
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, _rRange.GetOffset(), _rRange.GetCount(), _pData);
}

// -----------------------------------------------------------------------

int CIndexBufferAtomic::Bind(uint8 CB_UNUSED(_Stream), uint32 _Offset, uint32 CB_UNUSED(_Size))
{
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_BufferResourceId);
    
    glIndexPointer( s_NativeVertexFormat[m_Descriptor.GetFormat()], 
    				m_Descriptor.GetStride(),
                    BUFFER_OFFSET(_Offset));
    
    glEnableClientState(GL_INDEX_ARRAY);
    
    return glGetError();
}

// -----------------------------------------------------------------------

int CIndexBufferAtomic::Unbind()
{
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glDisableClientState(GL_INDEX_ARRAY);
    
    return glGetError();
}

CB_SUBNAMESPACE_END

#endif